package genetic.q3;

import java.util.Arrays;
import java.util.List;
import java.util.Vector;

import genetic.Genotype;
import genetic.Phenotype;

public class CliquePhenotype extends Phenotype {

	private int _size;
	private ReadFile _rf;
	public CliquePhenotype(){

	}

	public CliquePhenotype(int size, ReadFile rf){
		_size = size;
		_rf = rf;
	}

	@Override
	public Genotype createRandomInstance() {
		boolean[] instance = new boolean[_size];

		return new CliqueGenotype(instance);
	}

	@Override
	public Genotype[] crossover(Genotype g1, Genotype g2) {
		return crossovertest(g1, g2);
//		return new CliqueGenotype[]{new CliqueGenotype(new boolean[g1.getBitString().length]), new CliqueGenotype(new boolean[g2.getBitString().length])};
//		boolean[][] offsprings = singlePointCrossOver(g1.getBitString(), g2.getBitString());
//		if (!isClique(offsprings[0])) offsprings[0] = Arrays.copyOf(g1.getBitString(), _size);
//		if (!isClique(offsprings[1])) offsprings[1] = Arrays.copyOf(g2.getBitString(), _size);
//		return new CliqueGenotype[]{new CliqueGenotype(offsprings[0]), new CliqueGenotype(offsprings[1])};
	}

	public Genotype[] crossovertest(Genotype g1, Genotype g2){
		boolean[] g1rep = g1.getBitString();
		boolean[] g2rep = g2.getBitString();
		boolean[] offspring = new boolean[_size];
		for (int i=0; i<_size; i++){
			offspring[i] = g1rep[i] || g2rep[i];
		}
		Genotype os = new CliqueGenotype(offspring);
		double oFitness = fitness(os);
		double fitness1 = fitness(g1);
		double fitness2 = fitness(g2);
		if (oFitness == 0){
			return new Genotype[]{g1.clone(), g2.clone()};
		} else if (oFitness > fitness1 && oFitness>fitness2){
			double rand = Math.random()*(fitness1+fitness2);
			if (rand>fitness1){
				return new Genotype[]{os, g2.clone()};
			} else {
				return new Genotype[]{os, g1.clone()};
			}
		} else {
			return new Genotype[]{g1.clone(), g2.clone()};
		}
	}

	@Override
	public double fitness(Genotype g) {
		CliqueGenotype cg = (CliqueGenotype)g;

		int counter = 0;

		//count number of nodes
		for(int i = 0; i <_size; i++){
			if(cg.instance[i]){
				counter++;
			}
		}

		if(!isClique(g.getBitString()))
			counter=0;
		return counter;
	}

	private boolean isClique(boolean[] bitString){
		int wrong=0;
		Vector<Integer> nodes = new Vector<Integer>();
		//count number of nodes
		for(int i = 0; i <_size; i++){
			if(bitString[i]){
				nodes.add(i);
			}
		}

		for(int i = 0; i <nodes.size(); i++){
			//count number of missing arcs
			for(int j = i+1; j <nodes.size(); j++){
				if(!_rf.matrix[nodes.get(i)][nodes.get(j)]){
					wrong++;
				}
			}
		}
		return wrong==0;
	}

	@Override
	public Genotype mutate(Genotype g, double Pm) {
		boolean[] rep = Arrays.copyOf(g.getBitString(), g.getBitString().length);
		for (int loc=0; loc<rep.length; loc++){
		if (Math.random()<Pm){
//			int loc = (int)(Math.random()*rep.length);
			rep[loc] = !rep[loc];
			if (!isClique(rep)) rep[loc] = !rep[loc];
		}
		}
		return new CliqueGenotype(rep);
	}

	@Override
	public List<Genotype> getMatingPool(List<Genotype> population, int populationSize) {
		List<Genotype> newPopulation = new Vector<Genotype>();
		Genotype b = population.remove(0);
		newPopulation.add(b);
		while (!population.isEmpty()){
			int mindiff = Integer.MAX_VALUE;
			int best = -1;
			boolean[] tmp = b.getBitString();
			int diff = 0;
			for (int i=0; i<population.size(); i++){
				boolean[] tmp2 = population.get(i).getBitString();
				for (int j=0; j<tmp2.length; j++){
					if (tmp2[j]!=tmp[j]) diff++;
				}
				if (diff<mindiff) {
					mindiff = diff;
					best = i;
				}

			}
			b = population.remove(best);
			newPopulation.add(b);
		}
		return newPopulation;
//		return population;
//		return getMatingPoolRouletteWheel(population, populationSize);
	}

}
